﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EvoAlgLib
{
    public class Node
    {
        public Func _func;
        private Node _parent;
        private List<Node> _children = new List<Node>();
        private int _depth;
        

        public Node(Node other)
        {
            this._func = other._func;
            //this._parent = other._parent;
            this._depth = other._depth;
            foreach (Node n in other._children)
            {
                Node newnode = new Node(n);
                newnode.setParent(this);
                this._children.Add(newnode);
            }
        }

        public void setDepth(int depth)
        {
            this._depth = depth;
        }

        public void setParent(Node parent)
        {
            this._parent = parent;
        }

        public Node getParent()
        {
            return this._parent;
        }

        public Node(Func func)
        {
            this._func = func;
            for (int i = 0; i < this._func.getArity(); i++)
                this._children.Add(null);
        }
        public void setChild(Node n, int index)
        {
            n.setParent(this);
            this._children[index] = n;

        }

        public void addChild(Node n)
        {
            n.setParent(this);
            this._children.Add(n);
        }

        public void setLeft(Node n)
        {
            this.setChild(n, 0);
        }

        public List<Node> getChildren()
        {
            return this._children;
        }

        public void setChildren(List<Node> children)
        {
            this._children.Clear();
            foreach (Node n in children)
            {
                this._children.Add(n);
            }
        }

        public void setRight(Node n)
        {
            this.setChild(n, 1);
        }

        public int dexecute(double x, double y)
        {
            Dictionary<String, double> vars = new Dictionary<string, double>();
            vars["x"] = x;
            vars["y"] = y;
            try
            {
                if (execute(vars) > 0)
                    return 4;
                else
                    return -4;
            }
            catch (DivideByZeroException e)
            {
                return 0;
            }
        }

        public double execute(Dictionary<String, double> vars)
        {
            double[] args = new double[this._children.Count()];
            for (int i = 0; i < args.Length; i++)
            {
                args[i] = this._children[i].execute(vars);
            }
            this._func.setVars(vars);

            return this._func.getVal(args);
        }

        public void mutate(double pm)
        {
            int i = 0;
            foreach (Node ch in this._children)
            {
                ch.mutate(pm);
                i++;
            }
            if (i == 0)
                this._func.mutateVal(pm);
        }

        public override String ToString()
        {
            StringBuilder sb = new StringBuilder();
            if (this._func.getArity() > 0)
            {
                sb.Append("(");
                sb.Append(this._func.ToString());
                foreach (Node n in this._children)
                {
                    sb.Append(" ");
                    sb.Append(n.ToString());
                }
                sb.Append(")");
                return sb.ToString();
            }
            else
            {
                return this._func.ToString();
            }
        }

        public String ToNormalString()
        {
            /*
            StringBuilder sb = new StringBuilder();
            if (this._right != null)
            {
                sb.Append("(");
                sb.Append(this._left.ToNormalString());
                sb.Append(" ");
                sb.Append(this._func.ToString());
                sb.Append(" ");
                sb.Append(this._right.ToNormalString());
                sb.Append(")");
                return sb.ToString();
            }
            else if (this._left != null)
            {
                sb.Append("(");
                sb.Append(this._func.ToString());
                sb.Append(" ");
                sb.Append(this._left.ToNormalString());
                sb.Append(")");
                return sb.ToString();
            }
            else
                return this._func.ToString();
             */
            return "";
        }

        public int getDepth()
        {
            /*
            int l = 0;
            if (this._left != null)
                l = this._left.getDepth();
            int r = 0;
            if (this._right != null)
                r = this._right.getDepth();
            return Math.Max(l + 1, r + 1);
             */
            return this._depth;
        }

        public int getSize()
        {
            int l = 1;
            foreach (Node n in this._children)
                l += n.getSize();
            return l;
        }

        public Node getLeft()
        {
            return this._children[0];
        }

        public Node getRight()
        {
            return this._children[1];
        }

        public Node getRandomSubTree()
        {
            double p = LibStatics.getRandomizer().NextDouble();
            double q = Math.Pow(2, -(this.getDepth() - 1));
            if (p < q)
            {
                return this;
            }
            int r = LibStatics.getRandomizer().Next(this._children.Count());
            return this._children[r].getRandomSubTree();
        }
    }
}
